﻿using System;

public class ChannelSquare : PapuChannel
{
    internal static int[] dutyLookup = new int[] { 
        0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 
        0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1
     };
    internal int dutyMode;
    internal int envDecayCounter;
    internal bool envDecayDisable;
    internal bool envDecayLoopEnable;
    internal int envDecayRate;
    internal bool envReset;
    internal int envVolume;
    internal static int[] impLookup = new int[] { 
        1, -1, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 
        1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0, 0
     };
    internal bool isEnabled_Renamed_Field;
    internal int lengthCounter;
    internal bool lengthCounterEnable;
    internal int masterVolume;
    internal PAPU papu;
    internal int progTimerCount;
    internal int progTimerMax;
    internal int sampleValue;
    internal bool sqr1;
    internal int squareCounter;
    internal bool sweepActive;
    internal bool sweepCarry;
    internal int sweepCounter;
    internal int sweepCounterMax;
    internal int sweepMode;
    internal int sweepResult;
    internal int sweepShiftAmount;
    internal bool updateSweepPeriod;
    internal int vol;

    public ChannelSquare(PAPU papu, bool square1)
    {
        this.papu = papu;
        this.sqr1 = square1;
    }

    public virtual void clockEnvDecay()
    {
        if (this.envReset)
        {
            this.envReset = false;
            this.envDecayCounter = this.envDecayRate + 1;
            this.envVolume = 15;
        }
        else if (--this.envDecayCounter <= 0)
        {
            this.envDecayCounter = this.envDecayRate + 1;
            if (this.envVolume > 0)
            {
                this.envVolume--;
            }
            else
            {
                this.envVolume = this.envDecayLoopEnable ? 15 : 0;
            }
        }
        this.masterVolume = this.envDecayDisable ? this.envDecayRate : this.envVolume;
        this.updateSampleValue();
    }

    public virtual void clockLengthCounter()
    {
        if (this.lengthCounterEnable && (this.lengthCounter > 0))
        {
            this.lengthCounter--;
            if (this.lengthCounter == 0)
            {
                this.updateSampleValue();
            }
        }
    }

    public virtual void clockSweep()
    {
        if (--this.sweepCounter <= 0)
        {
            this.sweepCounter = this.sweepCounterMax + 1;
            if ((this.sweepActive && (this.sweepShiftAmount > 0)) && (this.progTimerMax > 7))
            {
                this.sweepCarry = false;
                if (this.sweepMode == 0)
                {
                    this.progTimerMax += this.progTimerMax >> this.sweepShiftAmount;
                    if (this.progTimerMax > 0xfff)
                    {
                        this.progTimerMax = 0xfff;
                        this.sweepCarry = true;
                    }
                }
                else
                {
                    this.progTimerMax -= (this.progTimerMax >> this.sweepShiftAmount) - (this.sqr1 ? 1 : 0);
                }
            }
        }
        if (this.updateSweepPeriod)
        {
            this.updateSweepPeriod = false;
            this.sweepCounter = this.sweepCounterMax + 1;
        }
    }

    public virtual void destroy()
    {
        this.papu = null;
    }

    public virtual void reset()
    {
        this.progTimerCount = 0;
        this.progTimerMax = 0;
        this.lengthCounter = 0;
        this.squareCounter = 0;
        this.sweepCounter = 0;
        this.sweepCounterMax = 0;
        this.sweepMode = 0;
        this.sweepShiftAmount = 0;
        this.envDecayRate = 0;
        this.envDecayCounter = 0;
        this.envVolume = 0;
        this.masterVolume = 0;
        this.dutyMode = 0;
        this.vol = 0;
        this.isEnabled_Renamed_Field = false;
        this.lengthCounterEnable = false;
        this.sweepActive = false;
        this.sweepCarry = false;
        this.envDecayDisable = false;
        this.envDecayLoopEnable = false;
    }

    public virtual void updateSampleValue()
    {
        if ((this.isEnabled_Renamed_Field && (this.lengthCounter > 0)) && (this.progTimerMax > 7))
        {
            if ((this.sweepMode == 0) && ((this.progTimerMax + (this.progTimerMax >> this.sweepShiftAmount)) > 0xfff))
            {
                this.sampleValue = 0;
            }
            else
            {
                this.sampleValue = this.masterVolume * dutyLookup[(this.dutyMode << 3) + this.squareCounter];
            }
        }
        else
        {
            this.sampleValue = 0;
        }
    }

    public virtual void writeReg(int address, int value_Renamed)
    {
        int num = this.sqr1 ? 0 : 4;
        if (address == (0x4000 + num))
        {
            this.envDecayDisable = (value_Renamed & 0x10) != 0;
            this.envDecayRate = value_Renamed & 15;
            this.envDecayLoopEnable = (value_Renamed & 0x20) != 0;
            this.dutyMode = (value_Renamed >> 6) & 3;
            this.lengthCounterEnable = (value_Renamed & 0x20) == 0;
            this.masterVolume = this.envDecayDisable ? this.envDecayRate : this.envVolume;
            this.updateSampleValue();
        }
        else if (address == (0x4001 + num))
        {
            this.sweepActive = (value_Renamed & 0x80) != 0;
            this.sweepCounterMax = (value_Renamed >> 4) & 7;
            this.sweepMode = (value_Renamed >> 3) & 1;
            this.sweepShiftAmount = value_Renamed & 7;
            this.updateSweepPeriod = true;
        }
        else if (address == (0x4002 + num))
        {
            this.progTimerMax &= 0x700;
            this.progTimerMax |= value_Renamed;
        }
        else if (address == (0x4003 + num))
        {
            this.progTimerMax &= 0xff;
            this.progTimerMax |= (value_Renamed & 7) << 8;
            if (this.isEnabled_Renamed_Field)
            {
                this.lengthCounter = this.papu.getLengthMax(value_Renamed & 0xf8);
            }
            this.envReset = true;
        }
    }

    public virtual bool Enabled
    {
        get
        {
            return this.isEnabled_Renamed_Field;
        }
        set
        {
            this.isEnabled_Renamed_Field = value;
            if (!value)
            {
                this.lengthCounter = 0;
            }
            this.updateSampleValue();
        }
    }

    public virtual int LengthStatus
    {
        get
        {
            if ((this.lengthCounter != 0) && this.isEnabled_Renamed_Field)
            {
                return 1;
            }
            return 0;
        }
    }
}

